package org.zjx.command.basic;

import lombok.Getter;
import lombok.SneakyThrows;
import org.zjx.command.Command;
import org.zjx.command.CommandFactory;
import org.zjx.core.IWindow;
import org.zjx.entity.GameMouse;
import org.zjx.state.StateManager;
import org.zjx.util.ImageUtil;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.List;
import java.util.Random;

public class GameMoveCommand extends Command {
    private int x;
    private int y;
    private final int distance;
    private final Rectangle targetRectangle;
    private final Rectangle screenRectangle;
    private final Point screenSize = new Point(816, 639);
    private final BufferedImage mouse = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_IMAGE);
    private final Random random = new Random();
    @Getter
    private GameMouse gameMouse;

    public GameMoveCommand(IWindow targetWindow, StateManager stateManager, Rectangle targetRectangle) {
        super(targetWindow, false, stateManager);
        this.targetRectangle = targetRectangle;
        this.screenRectangle = targetWindow.getBounds();
        this.distance = 8; // 矩形区域移动时容忍度可以稍大
    }

    public GameMoveCommand(IWindow targetWindow, StateManager stateManager, int x, int y, int distance) {
        super(targetWindow, false, stateManager);
        this.x = x;
        this.y = y;
        this.distance = Math.max(distance, 1);
        this.targetRectangle = null;
        this.screenRectangle = targetWindow.getBounds();
    }

    @Override
    protected void doExecute() throws Exception {
        // 如果当前没有鼠标位置信息，先使用基础移动初始化位置
        if (stateManager.getGameState().getGameMousePoint() == null) {
            Point initialTarget = getInitialTarget();
            moveMouseByBasic(initialTarget.x, initialTarget.y);
            CommandFactory.waitDirectly(targetWindow, 100); // 等待鼠标稳定
        }

        // 如果目标是矩形区域，移动到区域内的任意位置即可
        if (targetRectangle != null) {
            moveToRectangle(targetRectangle);
        } else {
            if (isRightBorder(x, y)) {
                moveRightBorder(x, y);
                return;
            }
            if (isBottomBorder(x, y)) {
                moveBottomBorder(x, y);
                return;
            }
            moveToPoint(x, y);
        }
    }

    /**
     * 获取初始目标点
     */
    private Point getInitialTarget() {
        if (targetRectangle != null) {
            // 矩形区域时移动到中心点
            return new Point(
                    (int) targetRectangle.getCenterX(),
                    (int) targetRectangle.getCenterY()
            );
        } else {
            return new Point(x, y);
        }
    }

    /**
     * 移动到矩形区域内的任意位置
     */
    private void moveToRectangle(Rectangle rectangle) throws Exception {
        moveWithImageRecognition((gameMouse, currentPos) -> {
            // 判断鼠标左上角坐标是否在目标区域内
            Point mouseTopLeft = new Point(gameMouse.rectangle.x, gameMouse.rectangle.y);
            return rectangle.contains(mouseTopLeft);
        }, (gameMouse, currentPos) -> {
            // 向区域中心点移动
            Point center = new Point(
                    (int) rectangle.getCenterX(),
                    (int) rectangle.getCenterY()
            );
            return calculateHumanLikeStep(currentPos, center, 0.7);
        });
    }

    /**
     * 移动到指定坐标点
     */
    @SneakyThrows
    private void moveToPoint(int x, int y) {
        Point target = new Point(x, y);
        moveWithImageRecognition((gameMouse, currentPos) -> {
            // 检查是否接近目标（使用鼠标左上角坐标）
            Point mouseTopLeft = new Point(gameMouse.rectangle.x, gameMouse.rectangle.y);
            int deltaX = target.x - mouseTopLeft.x;
            int deltaY = target.y - mouseTopLeft.y;
            return Math.abs(deltaX) < distance && Math.abs(deltaY) < distance;
        }, (gameMouse, currentPos) -> {
            // 计算拟人化移动步长（向目标点移动）
            return calculateHumanLikeStep(currentPos, target, 0.6);
        });
    }

    /**
     * 通用的图像识别移动逻辑
     */
    private void moveWithImageRecognition(
            MoveCondition condition,
            MoveStrategy strategy) throws Exception {

        int nullCount = 0;
        int moveCount = 0;

        while (true) {
            checkInterruption();

            BufferedImage screen = CommandFactory.screenshot(targetWindow);
            gameMouse = findMouse(screen);

            if (gameMouse == null) {
                nullCount = handleMouseNotFound(nullCount);
                CommandFactory.waitDirectly(targetWindow, 30);
                continue;
            }

            // 使用鼠标左上角坐标作为当前位置
            Point currentPos = new Point(gameMouse.rectangle.x, gameMouse.rectangle.y);

            // 检查是否满足停止条件
            if (condition.shouldStop(gameMouse, currentPos)) {
                break;
            }

            // 计算移动步长并执行移动
            Point step = strategy.calculateStep(gameMouse, currentPos);

            // 添加随机延迟，模拟人类反应时间
            if (moveCount > 0) {
                int delay = 20 + random.nextInt(30); // 20-50ms随机延迟
                CommandFactory.waitDirectly(targetWindow, delay);
            }

            moveMouseByBasic(stateManager.getGameState().getGameMousePoint().x + step.x, stateManager.getGameState().getGameMousePoint().y + step.y);
            moveCount++;

            // 偶尔添加微小停顿，模拟人类思考
            if (moveCount % 3 == 0 && random.nextDouble() < 0.3) {
                CommandFactory.waitDirectly(targetWindow, 10 + random.nextInt(20));
            }
        }
    }

    /**
     * 计算拟人化移动步长
     */
    private Point calculateHumanLikeStep(Point currentPos, Point target, double baseRatio) {
        int deltaX = target.x - currentPos.x;
        int deltaY = target.y - currentPos.y;

        // 基础步长比例
        double ratio = baseRatio;

        // 距离越远，移动比例越小（开始快速，接近时减速）
        double distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
        if (distance < 20) {
            ratio = 0.3 + random.nextDouble() * 0.2; // 接近时小步移动
        } else if (distance < 50) {
            ratio = 0.5 + random.nextDouble() * 0.2; // 中等距离
        }

        // 添加随机扰动，避免过于精确
        double randomFactor = 0.9 + random.nextDouble() * 0.2; // 0.9-1.1的随机因子
        ratio *= randomFactor;

        int stepX = (int) (deltaX * ratio);
        int stepY = (int) (deltaY * ratio);

        // 确保至少移动1像素
        if (Math.abs(stepX) == 0 && Math.abs(deltaX) > 0) {
            stepX = deltaX > 0 ? 1 : -1;
        }
        if (Math.abs(stepY) == 0 && Math.abs(deltaY) > 0) {
            stepY = deltaY > 0 ? 1 : -1;
        }

        return new Point(stepX, stepY);
    }

    /**
     * 处理鼠标找不到的情况
     */
    private int handleMouseNotFound(int nullCount) {
        nullCount++;
        if (nullCount > 5) {
            Point recoveryTarget = getInitialTarget();
            moveMouseByBasic(recoveryTarget.x, recoveryTarget.y);
            if (nullCount > 10) {
                moveMouseByBasic(screenSize.x / 2, screenSize.y / 2);
                nullCount = 0;
            }
        }
        return nullCount;
    }

    public void moveRightBorder(int x, int y) {
        moveToPoint(screenSize.x - mouse.getWidth(), y);
        CommandFactory.waitDirectly(targetWindow, 10);
        int intervalX = x - (screenSize.x - mouse.getWidth());
        Point currentPos = stateManager.getGameState().getGameMousePoint();
        if (currentPos != null) {
            moveMouseByBasic(currentPos.x + intervalX, currentPos.y);
        }
    }

    public void moveBottomBorder(int x, int y) {
        moveToPoint(x, screenSize.y - mouse.getHeight());
        CommandFactory.waitDirectly(targetWindow, 10);
        int intervalY = y - (screenSize.y - mouse.getHeight());
        Point currentPos = stateManager.getGameState().getGameMousePoint();
        if (currentPos != null) {
            moveMouseByBasic(currentPos.x, currentPos.y + intervalY);
        }
    }

    public boolean isRightBorder(int x, int y) {
        return x > screenSize.x - 8 - mouse.getWidth();
    }

    public boolean isBottomBorder(int x, int y) {
        return y > screenSize.y - 8 - mouse.getHeight();
    }

    private void moveMouseByBasic(int x, int y) {
        MoveCommand moveCommand = new MoveCommand(targetWindow, x, y, stateManager);
        moveCommand.execute();
    }

    //region 查找游戏鼠标坐标
    /**
     * 查找游戏鼠标坐标
     */
    public GameMouse findMouse(BufferedImage screen) {
        GameMouse gameMouse = findOrdinaryMouse(screen);
        if (gameMouse != null) return gameMouse;
        gameMouse = findNpcMouse(screen);
        if (gameMouse != null) return gameMouse;
        gameMouse = findMonsterMouse(screen);
        if (gameMouse != null) return gameMouse;
        gameMouse = findWriteMouse(screen);
        if (gameMouse != null) return gameMouse;
        gameMouse = findFingerMouse(screen);
        return gameMouse;
    }

    private GameMouse findOrdinaryMouse(BufferedImage screen) {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_IMAGE);
        List<Point> points = ImageUtil.matchTemplate(screen, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rectangle(point.x, point.y, image.getWidth(), image.getHeight()), GameMouse.Type.ORDINARY);
        }
        return null;
    }

    private GameMouse findNpcMouse(BufferedImage screen) {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_NPC_IMAGE);
        List<Point> points = ImageUtil.matchTemplate(screen, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rectangle(point.x, point.y, image.getWidth(), image.getHeight()), GameMouse.Type.NPC);
        }
        return null;
    }

    private GameMouse findMonsterMouse(BufferedImage screen) {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_MONSTER_IMAGE);
        List<Point> points = ImageUtil.matchTemplate(screen, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rectangle(point.x, point.y, image.getWidth(), image.getHeight()), GameMouse.Type.MONSTER);
        }
        return null;
    }

    private GameMouse findFingerMouse(BufferedImage screen) {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_FINGER_IMAGE);
        List<Point> points = ImageUtil.matchTemplate(screen, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rectangle(point.x, point.y, image.getWidth(), image.getHeight()), GameMouse.Type.FINGER);
        }
        return null;
    }

    private GameMouse findWriteMouse(BufferedImage screen) {
        BufferedImage image = ImageUtil.stringToBufferedImage(GameMouse.MOUSE_WRITE);
        List<Point> points = ImageUtil.matchTemplate(screen, image);
        if (!points.isEmpty()) {
            Point point = points.size() >= 2 ? points.get(1) : points.get(0);
            return new GameMouse(new Rectangle(point.x, point.y, image.getWidth(), image.getHeight()), GameMouse.Type.WRITE);
        }
        return null;
    }
    //endregion

    /**
     * 移动条件接口
     */
    @FunctionalInterface
    private interface MoveCondition {
        boolean shouldStop(GameMouse gameMouse, Point currentPos);
    }

    /**
     * 移动策略接口
     */
    @FunctionalInterface
    private interface MoveStrategy {
        Point calculateStep(GameMouse gameMouse, Point currentPos);
    }
}